#include <stdio.h>
#include <math.h>
#include <algorithm>

#include <iostream>
#include <fstream>

#include "Auxiliary.h"

#define ERR_REMAINDER() printf("Please input valid data: ")

char CheckedInputChar( const char* p_validityChars, const unsigned int p_cnt )
{
    char input = -1;
    
    input = getchar();
    fflush(stdin);
    for ( ; p_validityChars+p_cnt == std::find(p_validityChars, p_validityChars+p_cnt, input); )
    {
        ERR_REMAINDER();
        input = getchar();
        fflush(stdin);
    }

    return input;
}

int CheckedInputInt( const int p_min, const int p_max )
{
    int input = p_min-1;

    scanf_s("%d", &input);
    fflush(stdin);
    for ( ; input<p_min || input>p_max; )
    {
        ERR_REMAINDER();
        scanf_s("%d", &input);
        fflush(stdin);
    }

    return input;
}

float CheckedInputFloat( const float p_min, const float p_max )
{
    float input = -LIMIT_EPSILON;

    scanf_s("%f", &input);
    fflush(stdin);
    for ( ; input<p_min || input>p_max; )
    {
        ERR_REMAINDER();
        scanf_s("%f", &input);
        fflush(stdin);
    }

    return input;
}

bool isConverged( const double x0, const double x1)
{
	return fabs( x0 - x1 ) < LIMIT_EPSILON;
}

//static std::ofstream of("output.txt", std::ofstream::out);

void logStep( const unsigned int i, const double x0, const double x1 )
{
	printf("iteration step %2d, x%d \t= %1.12f, x%d \t= %1.12f \n", i+1, i, x0, i+1, x1);
    
    //of.precision(15);
    //of<<i<<"\t"<<x0<<"\t"<<x1<<std::endl;

    return;
}

void FinalLog( const unsigned int p_i, const double p_x0, const double p_x1 )
{
 	if ( !isConverged(p_x0, p_x1) )
	{
		printf("\nStopped for divergening...\n");
	}
	else
	{
        printf("\nConverged with %d steps, reaches at x = %1.12f \n", p_i, p_x1);
    }
	
    return;
}

bool ItrProcess( const itrFunctor p_itrFunctor,
                 const double p_initValue,
                 const double p_epsilong /*= LIMIT_EPSILON*/,
                 const bool p_bLog /*= true*/ )
{
    double x1 = p_initValue;
    double x0 = 0.0;
    unsigned int i = 0;

    do
    {
        x0 = x1;
        x1 = p_itrFunctor(x0);

        if ( p_bLog )
        {
            logStep( ++i, x0, x1 );
        }

    } while( !isConverged( x0, x1 ) && abs(x1)<FLT_MAX);

    FinalLog( i, x0, x1 );

    return false;
}

unsigned int ChoiceC( const unsigned int r, const unsigned int n )
{
    if ( r == 0 ) return 1;

    if ( r > n/2 )
    {
        return ChoiceC(n-r, n);
    }

    unsigned int n_r = 1; //n*.....*(n-r)
    for ( unsigned int i = n; i > n-r; --i )
    {
        n_r *= i;
    }

    unsigned int r_1 = 1; //r!
    for ( unsigned int i = r; i > 0; --i )
    {
        r_1 *= i;
    }

    return n_r/r_1;
}

int* BinomalCoff( const unsigned int n )
{
    unsigned int cnt = n+1;
    int* ret = new int[cnt];

    for ( unsigned int i = 0; i < cnt; ++i )
    {
        ret[i] = ChoiceC(i, n);
    }

    return ret;
}

